import * as PouchDB from 'pouchdb';
import findPlugin from "pouchdb-find";
import moment from 'moment';
import { Config } from './models/Config';
import { City } from './models/City';
import { Role } from './models/Role';
import { Staff } from './models/Staff';
import { Order } from './models/Order';
import { OrderState } from '../objects/internal/OrderState';

export const NUMBERS = '0123456789';
export const LETTERS = 'abcdefghikjlmnopqrstuvwxyz';
export const LOWERCASE_NUMERIC = NUMBERS + LETTERS;

PouchDB.default.plugin(findPlugin);

export function randomString(length = 16, alphabet = LOWERCASE_NUMERIC): string {
	let out = '';
	for (let i = 0; i < length; i++) {
		out += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
	}
	return out;
}

function generateMockCities(): City[] {
	// Get coordinates here: https://api.map.baidu.com/lbsapi/getpoint/index.html
	return [
		{
			name: '桂林',
			region: [
				{lng: 110.41638, lat: 25.310535},
				{lng: 110.426262, lat: 25.3105},
				{lng: 110.426154, lat: 25.31308},
				{lng: 110.431104, lat: 25.317886},
				{lng: 110.431104, lat: 25.317886},
				{lng: 110.426199, lat: 25.324516},
				{lng: 110.424923, lat: 25.326622},
				{lng: 110.420001, lat: 25.326622},
				{lng: 110.413263, lat: 25.316155},
			]
		}
	];
}

function generateMockRoles(): Role[] {
	return [
		{ _id: 'system', name: '超级管理员' },
		{ _id: 'admin', name: '管理员' },
		{ _id: 'staff', name: '员工' },
		{ _id: 'marketing', name: '市场' },
		{ _id: 'accounting', name: '会计' }
	];
}

function generateMockStaffs(): Staff[] {
	const hash = '5597b79cc1fbc43bbfc1fb22094d70e8507c093dd2c62d6a2c5f214e56716f13'; // 123456 ; aaaaaaaaaaaaaaaa

	return [
		{ _id: 'TestAcct', username: 'TestAcct', password: hash, salt: 'aaaaaaaaaaaaaaaa', displayName: '测试账号', gender: '', role: 'system', registerTime: (new Date()).toISOString(), clockInTime: "-", notes: '' },
		{ _id: 'Worker', username: 'worker', password: hash, salt: 'aaaaaaaaaaaaaaaa', displayName: '测试员工', gender: '', role: 'staff', registerTime: (new Date()).toISOString(), clockInTime: "9:00", notes: '' }
	];
}

function generateMockOrders(): Order[] {
	let out: Order[] = [];
	for (let i = 0; i < 50; i++) {
		const mileage = 1000 + 2000 * Math.random();
		const duration = 5 * Math.random() + mileage / 4.16;
		out.push({
			_id: randomString(),
			bikeId: 'BK' + randomString(6),
			username: randomString(3, LETTERS.toUpperCase()),
			phone: '1' + randomString(10, NUMBERS),
			mileage: mileage,
			duration: duration,
			state: OrderState.CLOSED,
			startTime: moment().subtract(duration, 'second').toDate(),
			subtotal: 2 + mileage / 1000,
			total: 2 + mileage / 1200,
			startPosition: {lng: 110.425817, lat: 25.315032},
			currentPosition: {lng: 110.425817, lat: 25.315032}
		});
	}
	return out;
}

export class Database {
	public getCollection<T>(name: string) {
		return this.collections[name] as PouchDB.Database<T>;
	}

	public async migrate() {
		const config = new PouchDB.default<Config>('config');
		let doc;
		try {
			doc = await config.get('configured');
		} catch(e) { }
		if (!doc || doc.value !== this.revisionCount) {
			console.log('migrating');
			console.log('deleting everything');
			await this.destroyAndRebuild();
			let roles = this.getCollection('roles');
			let cities = this.getCollection('cities');
			let staffs = this.getCollection('staffs');
			let orders = this.getCollection('orders');
			console.log('populating');
			// start migration
			await Promise.all(generateMockCities().map(x => cities.post(x)));
			await Promise.all(generateMockRoles().map(x => roles.post(x)));
			await Promise.all(generateMockStaffs().map(x => staffs.post(x)));
			await Promise.all(generateMockOrders().map(x => orders.post(x)));
			if (doc) {
				await config.put({_id: 'configured', _rev: doc._rev, value: this.revisionCount});
			} else {
				await config.put({_id: 'configured', value: this.revisionCount});
			}
			console.log('done');
		} else {
			console.log('migration already done');
		}
	}

	private collections: Record<string, PouchDB.Database> = {
		roles: new PouchDB.default<Role>('roles'),
		cities: new PouchDB.default<City>('cities'),
		staffs: new PouchDB.default<Staff>('staffs'),
		orders: new PouchDB.default<Order>('orders'),
	};

	private async destroyAndRebuild() {
		await Promise.all(Object.keys(this.collections).map(name => this.getCollection(name).destroy()));
		Object.keys(this.collections).forEach(name => this.collections[name] = new PouchDB.default(name));
	}

	private revisionCount = '7';
}
